#include <boost/thread/thread.hpp>
#include <boost/thread/condition_variable.hpp>

#include <boost/thread.hpp>

#include <iostream>
#include <vector>

#include <stdio.h>
#include <unistd.h>
#include <string>


#include "integral.hpp"
#include "config.hpp"

using namespace std;


int main(int argc, char* argv[])
{
    int nThreads;

    if ( (argc != 2) || ( sscanf( argv[1],"%d",&nThreads ) != 1) || ( nThreads < 1))
    {
        cout << "Wrong arguments. Cancel." << endl;
	    return 1;
    }
    
    boost::thread tmp_thread;
    if ( (uint)nThreads > tmp_thread.hardware_concurrency()*100)
    {
        std::cout << "Available only:" << tmp_thread.hardware_concurrency() << " CPU  but requested " << nThreads << " threads.  Cancel." << std::endl;
        return 1;
    }

    
    cout << "Integrate: " << _FUNC_STRING << " from: " << _A << " to: " << _B << " with precision: " << _PRECISION << endl;
////////////////
    Monitor< data_t> monitor_task  ( 2);
    Monitor< double> monitor_result( nThreads);

    boost::thread_group threads;

    ThreadFunc *funcs =( ThreadFunc*) new ThreadFunc[ nThreads];

    for ( int i = 0; i < nThreads; i++)
    {
        funcs[ i].Init( monitor_task, monitor_result);

        try
        {
            threads.create_thread( funcs[i]);
        }
        catch( ...)
        {
            cout << "Can't create " << i+1 << " threads. Cancel. " << endl;
            for ( int j = 0; j < i+1; j++)
            {
               threads.interrupt_all();
               threads.join_all();
            }
            return 1;
        }
    }

    for ( int i = 0; i < nThreads; i++)
    {
        data_t tmp;

        double delta = ( _B - _A) / nThreads;

        tmp.a =  _A + delta * i;
        tmp.b =  tmp.a + delta;
        tmp.precision = _PRECISION;

        //cout << "A " << tmp.a << " B " << tmp.b << endl;
        monitor_task.Send( tmp);
    }
    double res;
    for ( int i = 0; i < nThreads; i++)
    {
        double tmp = monitor_result.Receive();

        //cout << "preRes: " << tmp.precision << endl;

        res += tmp;

        data_t tmp1;

        tmp1.precision = -1.0;
        monitor_task.Send( tmp1);

    }
    threads.join_all();
    cout << "Result: " << res << endl;

    //tmp = " ";
    //cout << tasks.size() << tmp << results.size() << endl;



    delete[] funcs;

    return 0;
}

